package org.wheel.module.core.security;

import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.core.util.XmlUtil;
import cn.hutool.http.HtmlUtil;
import cn.hutool.json.JSONUtil;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
 * SQL安全工具类
 * 提供SQL注入防护、标识符验证等安全相关功能
 */
public class SqlSecurityUtils {

    // 安全字段白名单（根据业务动态添加）
    private static final Set<String> SAFE_FIELDS = new HashSet<String>() {};

    // 危险关键字列表
    private static final Set<String> DANGEROUS_KEYWORDS = new HashSet<String>() {{
        add("select");
        add("insert");
        add("update");
        add("delete");
        add("drop");
        add("create");
        add("alter");
        add("exec");
        add("execute");
        add("union");
        add("join");
        add("into");
        add("from");
        add("where");
        add("group");
        add("order");
        add("having");
        add("script");
        add("javascript");
        add("vbscript");
        add("onload");
        add("onerror");
        add("alert");
        add("confirm");
        add("prompt");
        add("declare");
        add("cast");
        add("convert");
        add("waitfor");
        add("delay");
    }};

    // 合法的操作符列表
    private static final Set<String> VALID_OPERATORS = new HashSet<String>() {{
        add("=");
        add("eq");
        add("!=");
        add("<>");
        add("ne");
        add("neq");
        add(">");
        add("gt");
        add(">=");
        add("ge");
        add("gte");
        add("<");
        add("lt");
        add("<=");
        add("le");
        add("lte");
        add("like");
        add("in");
        add("not in");
        add("notin");
        add("between");
        add("is");
        add("is not");
        add("isnot");
    }};

    /**
     * 安全处理SQL值（值转义）
     * @param value 需要转义的SQL值
     * @return 转义后的SQL值
     */
//    public static String escapeSqlValue(String value) {
//        if (StrUtil.isBlank(value)) {
//            return value;
//        }
//        // 仅转义单引号 (主要防御点)
//        return StrUtil.replace(value, "'", "''");
//    }
    /**
     * SQL 值转义 + 包引号（一体完成）
     * 返回可直接嵌入 SQL 的字面量字符串（含单引号）
     */
    public static String escapeSqlValue(String value) {
        if (value == null) return "NULL";
        // 1. 转义单引号
        String escaped = value.replace("'", "''");
        // 2. 包引号
        return "'" + escaped + "'";
    }

    /**
     * 把业务 SQL 预处理成 CCJSqlParser 可解析的「安全字面量」
     * 1. 全角符号 → 半角
     * 2. 连续空白 → 单空格
     * 3. 反斜杠 → 双反斜杠（转义）
     * 4. 中文标点 → 英文标点
     * @param raw
     * @return
     */
    public static String safeSql(String raw){
        if (StrUtil.isBlank(raw)) return raw;

        // ① 全角 → 半角（中文括号、引号、逗号、分号等）
        raw = CharSequenceUtil.replaceChars(raw,
                "，。；：“”‘’（）【】",
                ",.;:\"\"''()[]");

        // ② 连续空白（含 \t \n \r）→ 单空格
        raw = raw.replaceAll("\\s+", " ");

        // ③ 反斜杠 → 双反斜杠（防止 \\n 被当成换行）
        raw = raw.replace("\\", "\\\\");

        // ④ 去掉首尾空格
        return raw.trim();
    }

    /**
     * 综合转义函数
     * @param input 输入字符串
     * @param type 转义类型（REGEX, HTML, JSON）
     */
    public static String escape(String input, EscapeType type) {
        return switch (type) {
            case URL -> URLUtil.encode(input);
            case XML -> XmlUtil.escape(input);
            case REGEX -> ReUtil.escape(input);
            case HTML -> HtmlUtil.escape(input);
            case JSON -> JSONUtil.escape(input);
            default -> input;
        };
    }

    /**
     * 安全处理SQL标识符（字段名/表名）
     * @param identifier SQL标识符（如字段名、表名）
     * @return 处理后的SQL标识符
     */
    public static String escapeSqlIdentifier(String identifier) {
        if (StrUtil.isBlank(identifier)) {
            throw new SecurityException("SQL标识符不能为空");
        }

        // 1. 白名单验证
        if (SAFE_FIELDS.contains(identifier.toLowerCase())) {
            return identifier;
        }

        // 2. 格式验证 (字母/数字/下划线)
        if (!ReUtil.isMatch("^[a-zA-Z0-9_]+$", identifier)) {
            throw new SecurityException("非法SQL标识符: " + identifier);
        }

        // 3. 添加反引号包裹
        return "`" + identifier + "`";
    }

    /**
     * 验证SQL条件连接符
     * @param operator SQL操作符
     * @return 是否为合法的操作符
     */
    public static boolean isValidOperator(String operator) {
        if (StrUtil.isBlank(operator)) {
            return false;
        }
        return VALID_OPERATORS.contains(operator.toLowerCase());
    }

    /**
     * 验证字段名是否合法
     * @param field 字段名
     * @return 是否合法
     */
    public static boolean isValidFieldName(String field) {
        if (StrUtil.isBlank(field)) {
            return false;
        }

        // 检查是否在白名单中
        if (SAFE_FIELDS.contains(field.toLowerCase())) {
            return true;
        }

        // 检查格式是否符合要求  可能有 <别名.字段名> 的输出
        return ReUtil.isMatch("^[a-zA-Z0-9_\\.]+$", field);
    }

    /**
     * SQL关键字过滤（额外防护层）
     * @param str 需要检查的字符串
     * @return 是否包含危险关键字
     */
    public static boolean containsDangerousKeywords(String str) {
        if (StrUtil.isBlank(str)) {
            return false;
        }

        String lowerStr = str.toLowerCase();
        return DANGEROUS_KEYWORDS.stream().anyMatch(lowerStr::contains);
    }

    /**
     * 添加安全字段到白名单
     * @param field 安全字段名
     */
    public static void addSafeField(String field) {
        if (StrUtil.isNotBlank(field)) {
            SAFE_FIELDS.add(field.toLowerCase());
        }
    }

    /**
     * 批量添加安全字段到白名单
     * @param fields 安全字段名集合
     */
    public static void addSafeFields(Set<String> fields) {
        if (fields != null) {
            fields.forEach(field -> {
                if (StrUtil.isNotBlank(field)) {
                    SAFE_FIELDS.add(field.toLowerCase());
                }
            });
        }
    }

    /**
     * 获取只读的安全字段白名单
     * @return 安全字段白名单的不可变视图
     */
    public static Set<String> getSafeFields() {
        return Collections.unmodifiableSet(SAFE_FIELDS);
    }

    /**
     * 获取只读的危险关键字列表
     * @return 危险关键字列表的不可变视图
     */
    public static Set<String> getDangerousKeywords() {
        return Collections.unmodifiableSet(DANGEROUS_KEYWORDS);
    }

    /**
     * 获取只读的合法操作符列表
     * @return 合法操作符列表的不可变视图
     */
    public static Set<String> getValidOperators() {
        return Collections.unmodifiableSet(VALID_OPERATORS);
    }
}
